﻿
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;
using CubicSplines;

/// <summary>
/// An aggregate to calculate the p-value for the Jarque-Bera goodness-of-fit test of composite normality
/// (with Microsoft SQL Server)
/// </summary>
/// <remarks>
/// Returns 0.001 when the p-value &lt;= 0.001, and 0.5 when the p-value &gt;= 0.5.
/// Ignores NULL's. Returns NULL, when no calculation is possible. Uses a single-pass algorithm.
/// Note, that a multipass algorithm is preferable for calculating moments of the second (and any higher) order. See
/// <para>
/// Numerical recipes in C: the art of scientific computing,
/// W.H. Press et al., Cambridge University Press, 2002, ISBN 0-521-43108-5.
/// </para>
/// </remarks>
[Serializable]
[SqlUserDefinedAggregate(Format.Native, IsInvariantToNulls = true, IsInvariantToDuplicates = false, IsInvariantToOrder = true)]
public struct JBTest
{

    UInt64 _n;
    double _s1;
    double _s2;
    double _s3;
    double _s4;

    /// <summary>
    /// Calculates the p-value, given the value of Jarque-Bera statistics.
    /// </summary>
    /// <param name="JBSt">
    /// An actual Jarque-Bera statistics of the sample
    /// </param>
    /// <remarks>
    /// First, given the sample size _n, an array of the Jarque-Bera test critical values
    /// is calculated for all the tabulated p-values.
    /// Second, the result is approximated using the array obtained at the previous step
    /// using the actual Jarque-Bera statistics of the sample.
    /// </remarks>
    private SqlDouble CalculatePValue(double JBSt)
    {

        // Tabulated p-values
        double[] pvals =
        {
            0.50, 0.25, 0.2, 0.15, 0.1, 0.059948, 0.05, 0.035938, 0.025, 0.021544,
            0.012915, 0.01, 0.0077426, 0.0046416, 0.0027826, 0.0016681, 0.001
        };

        // Tabulated values of 1/ln(n), where n is the sample size
        double[] inv_log_n =
        {
            0E+0, 1.31563E-1, 1.33413E-1, 1.35543E-1, 1.38041E-1, 1.41042E-1,
            1.44765E-1, 1.49597E-1, 1.60911E-1, 1.66904E-1, 1.75322E-1, 1.81111E-1,
            1.88739E-1, 1.93619E-1, 1.99575E-1, 2.07112E-1, 2.17147E-1, 2.22232E-1,
            2.28205E-1, 2.35377E-1, 2.44239E-1, 2.55622E-1, 2.62697E-1, 2.71085E-1,
            2.81266E-1, 2.94014E-1, 3.10667E-1, 3.33808E-1, 3.69269E-1, 4.34294E-1,
            6.21335E-1, 7.21348E-1, 9.10239E-1
        };

        // Precomputed values of d2s/d(1/ln(n))2 versus 1/ln(n) (here s's are the critical values of
        // the Jarque-Bera test and n's are the sample sizes) for each tabulated p-value
        double[][] second_derivatives =
        {
            new double[]
            {
                0E+0, 1.31234E+3, 2.53389E+3, 1.53661E+3, 1.44423E+3, 2.39102E+3,
                1.25735E+2, 1.42488E+3, 4.57589E+2, -2.08636E+2, -1.12865E+2,
                -9.80203E+2, -3.82486E+2, -2.14768E+3, -5.39165E+2, -1.31049E+3,
                -3.69573E+3, 1.95639E+3, -4.12011E+3, -1.25034E+3, -1.23245E+3,
                -2.20338E+3, -1.12967E+3, -1.36839E+3, -1.00722E+3, -8.33956E+2,
                -5.33812E+2, -1.83135E+2, 2.51006E+2, 4.37456E+2, 6.36714E+1,
                2.2253E+1, 0E+0
            },
            new double[]
            {
                0E+0, 1.00278E+3, 2.32239E+3, 1.26907E+3, 8.97163E+2, 1.71123E+3,
                5.21911E+2, 8.76581E+2, 1.77126E+2, -1.74724E+2, 8.26737E+1,
                -8.92437E+2, -6.14936E+2, -1.31187E+3, -4.6075E+2, -1.19278E+3,
                -2.47741E+3, 9.07927E+2, -2.64719E+3, -8.275E+2, -1.33321E+3,
                -1.33331E+3, -7.76486E+2, -1.11359E+3, -7.56861E+2, -5.75736E+2,
                -4.72547E+2, -1.24779E+2, 1.37711E+2, 3.30246E+2, 6.92283E+1,
                2.01931E+1, 0E+0
            },
            new double[]
            {
                0E+0, 7.40954E+2, 1.39021E+3, 1.88429E+3, 3.31727E+2, 1.17746E+3,
                4.82749E+2, 5.37343E+2, 1.67874E+2, -1.26588E+2, -2.02638E+2,
                -5.04918E+2, -6.99291E+2, -8.00057E+2, -4.0736E+2, -1.13631E+3,
                -1.20798E+3, -2.31902E+2, -1.56996E+3, -6.23002E+2, -9.6134E+2,
                -1.04329E+3, -5.42053E+2, -7.0727E+2, -5.98298E+2, -4.47311E+2,
                -2.90367E+2, -1.2101E+2, 8.16392E+1, 2.35242E+2, 7.03214E+1,
                1.85179E+1, 0E+0
            },
            new double[]
            {
                0E+0, 5.32959E+2, -3.64921E+2, 2.94136E+3, -5.33164E+2, 1.11635E+3,
                2.76529E+2, 4.98106E+2, 2.64092E+0, -1.23299E+2, -2.11133E+2,
                -3.91506E+2, -5.44854E+2, -5.66442E+2, -3.72967E+2, -8.21179E+2,
                -7.85884E+2, -4.92431E+2, -8.33089E+2, -5.63445E+2, -7.05395E+2,
                -6.27473E+2, -3.58466E+2, -6.14748E+2, -3.45635E+2, -3.30657E+2,
                -1.90204E+2, -8.13007E+1, 4.84754E+1, 1.58998E+2, 6.53999E+1,
                1.73778E+1, 0E+0
            },
            new double[]
            {
                0E+0, 3.49598E+2, -4.56799E+2, 1.59291E+3, 1.57544E+2, 5.32129E+2,
                3.3061E+2, 3.35955E+2, 2.87821E+1, -1.23922E+2, -1.71701E+2, -3.30849E+2,
                -3.84903E+2, -4.1038E+2, -3.30915E+2, -6.46333E+2, -3.67569E+2,
                -3.99418E+2, -7.5148E+2, -2.54632E+2, -4.85904E+2, -5.10326E+2,
                -1.51976E+2, -3.91917E+2, -2.65993E+2, -1.87078E+2, -1.30446E+2,
                -4.6907E+1, 3.84154E+1, 1.00606E+2, 5.59376E+1, 1.63947E+1, 0E+0
            },
            new double[]
            {
                0E+0, 2.58729E+2, 2.51958E+2, 2.62148E+2, 8.42884E+2, 1.65492E+2,
                2.93477E+2, 3.30332E+2, 3.0792E+0, -6.73224E+1, -1.53389E+2, -3.50736E+2,
                -2.57785E+2, -3.58426E+2, -2.70518E+2, -5.73107E+2, -3.01151E+2,
                -3.9629E+2, -4.54509E+2, -3.30836E+2, -3.3188E+2, -4.48179E+2,
                -9.25244E+1, -3.187E+2, -2.18847E+2, -1.31109E+2, -1.11099E+2,
                -3.04522E+1, 3.79515E+1, 7.82406E+1, 4.9796E+1, 1.58667E+1, 0E+0
            },
            new double[]
            {
                0E+0, 1.90051E+2, -5.84873E+1, 3.89385E+2, 5.76641E+2, 3.13386E+1,
                3.61891E+2, 2.17306E+2, 4.51175E+1, -6.87859E+1, -8.08428E+1, -3.44428E+2,
                -2.3161E+2, -2.11583E+2, -2.8048E+2, -4.58286E+2, -2.33886E+2, -3.6204E+2,
                -3.70912E+2, -2.20291E+2, -3.05235E+2, -3.50301E+2, -3.64427E+1,
                -2.52606E+2, -1.87812E+2, -8.89426E+1, -8.25413E+1, -2.09992E+1,
                3.7929E+1, 6.07972E+1, 4.28147E+1, 1.52838E+1, 0E+0
            },
            new double[]
            {
                0E+0, 7.95962E+1, -2.28914E+2, 2.73218E+2, 3.09845E+2, -8.64873E+1,
                1.30437E+2, 1.25015E+2, 4.57076E+1, -3.11978E+1, -2.142E+1, -1.97323E+2,
                -1.75525E+2, -8.17904E+1, -2.34527E+2, -2.58981E+2, -1.58596E+2,
                -2.38652E+2, -2.34863E+2, -1.22636E+2, -1.79482E+2, -2.01252E+2,
                -4.02324E+1, -1.32091E+2, -9.14951E+1, -4.97336E+1, -4.04113E+1,
                -1.38909E+0, 3.40855E+1, 3.87439E+1, 2.76727E+1, 1.37217E+1, 0E+0
            },
            new double[]
            {
                0E+0, 5.79943E+1, -3.30461E+2, 3.09258E+2, 1.43555E+2, -5.71159E+1,
                7.86261E+1, 7.98992E+1, 3.58195E+1, -7.16796E+0, -4.89641E+1, -1.1429E+2,
                -1.68399E+2, -6.66151E+1, -1.88744E+2, -2.3745E+2, -9.16542E+1,
                -2.97489E+2, -1.24977E+2, -1.34296E+2, -1.51799E+2, -1.4921E+2,
                -6.41041E+1, -9.34161E+1, -7.51528E+1, -3.74012E+1, -3.28557E+1,
                4.5261E+0, 3.16836E+1, 3.49719E+1, 2.33217E+1, 1.31108E+1, 0E+0
            },
            new double[]
            {
                0E+0, 6.76537E+0, -4.89049E+1, -9.58689E+1, 2.49028E+2, -2.07177E+2,
                1.31164E+1, 4.3194E+0, -2.20403E+1, -2.88108E+1, -4.10254E+0, -7.16127E+1,
                -7.29732E+1, -6.1155E+1, -6.58839E+1, -1.55275E+2, -6.56563E+1,
                -1.81987E+2, -6.8191E+1, -1.02109E+2, -7.03259E+1, -1.1699E+2,
                -1.90668E+0, -7.73028E+1, -2.59809E+1, -2.37104E+1, -5.71136E+0,
                9.06187E+0, 2.73802E+1, 2.81546E+1, 1.40971E+1, 1.11484E+1, 0E+0
            },
            new double[]
            {
                0E+0, -1.71852E+1, -2.45421E+2, 2.66056E+1, 7.91167E+1, -2.16172E+2,
                -2.35347E+1, -3.83483E+1, -6.68968E+1, -4.41349E+1, -2.24167E+1,
                -7.09849E+1, -5.19814E+0, -7.33995E+1, 7.18319E+0, -9.09478E+1,
                -8.83201E+0, -1.17393E+2, -7.15139E+0, -7.48176E+1, -2.04642E+1,
                -7.85144E+1, 1.14572E+1, -3.13979E+1, -1.55242E+1, -3.80502E+0,
                5.35482E+0, 1.42449E+1, 2.37141E+1, 2.32487E+1, 8.21666E+0, 8.75312E+0,
                0E+0
            },
            new double[]
            {
                0E+0, -2.43613E+1, -3.66958E+2, 1.18444E+2, -5.40636E+1, -1.6314E+2,
                -4.07323E+1, -6.18737E+1, -7.47583E+1, -5.70456E+1, -3.5549E+1,
                -5.62412E+1, -2.31324E+1, -4.33565E+1, -4.27667E+0, -5.06755E+1,
                3.95616E+0, -9.30436E+1, 4.03371E+1, -6.59937E+1, 1.10144E+0, -4.93407E+1,
                1.69827E+1, -1.51769E+1, -1.05169E+1, 7.24632E+0, 7.42181E+0, 1.72931E+1,
                2.1761E+1, 2.07594E+1, 6.0966E+0, 7.23964E+0, 0E+0
            },
            new double[]
            {
                0E+0, -3.60687E+1, -4.34112E+2, 1.57407E+2, -1.23673E+2, -1.3213E+2,
                -5.81527E+1, -8.07321E+1, -8.23063E+1, -5.29981E+1, -6.27208E+1,
                -3.80781E+1, -3.44968E+1, -3.01934E+1, -1.98796E+1, -7.6753E+0,
                2.74069E-1, -2.02294E+1, 3.44215E+1, -7.86473E+0, 2.43422E+1, 4.94971E+0,
                3.47147E+1, 1.59663E+1, 1.9808E+1, 2.00899E+1, 2.17605E+1, 1.91558E+1,
                1.84817E+1, 1.38665E+1, 4.09713E+0, 2.76988E+0, 0E+0
            },
            new double[]
            {
                0E+0, -3.44135E+1, -4.02377E+2, 1.39555E+2, -1.26165E+2, -7.50366E+1,
                -1.03979E+2, -4.943E+1, -7.88999E+1, -4.74052E+1, -3.69068E+1,
                -5.83605E+1, -3.39474E+0, -2.61308E+1, -2.17739E+1, 1.45089E+0,
                -1.95716E+0, -1.2445E+0, 2.39431E+1, 2.64517E+0, 2.39025E+1, 1.0988E+1,
                3.41203E+1, 1.65142E+1, 2.44847E+1, 2.1056E+1, 2.31794E+1, 1.9375E+1,
                1.70108E+1, 9.64253E+0, 3.83428E+0, 1.44386E+0, 0E+0
            },
            new double[]
            {
                0E+0, -3.28157E+1, -2.03996E+2, -1.97096E+0, -4.81746E+1, -9.70515E+1,
                -6.1507E+1, -5.1196E+1, -5.92198E+1, -4.05819E+1, -3.09126E+1,
                -3.91792E+1, -1.16294E+1, -1.70059E+1, -1.3362E+1, -5.58719E-1, 4.3906E-1,
                3.39265E+0, 1.79007E+1, 6.43421E+0, 1.85587E+1, 1.39547E+1, 2.47277E+1,
                1.63966E+1, 1.93976E+1, 1.9885E+1, 1.86786E+1, 1.67741E+1, 1.40605E+1,
                7.8467E+0, 2.98702E+0, 1.29103E+0, 0E+0
            },
            new double[]
            {
                0E+0, -2.82995E+1, -1.80793E+2, 1.55025E+1, -4.02472E+1, -8.13702E+1,
                -6.06308E+1, -3.61381E+1, -4.78442E+1, -3.42612E+1, -3.07929E+1,
                -2.51086E+1, -4.95189E+0, -3.00119E+1, 3.17831E+0, -7.93811E+0,
                8.51551E+0, -7.73568E+0, 2.12368E+1, 3.04692E+0, 1.72283E+1, 9.15042E+0,
                2.25241E+1, 1.18421E+1, 1.75105E+1, 1.54906E+1, 1.5814E+1, 1.36573E+1,
                1.1508E+1, 6.82023E+0, 1.88318E+0, 1.75578E+0, 0E+0
            },
            new double[]
            {
                0E+0, -1.19976E+1, -7.33823E+1, 6.24888E+1, -5.53405E+1, -2.09674E+1,
                -8.30497E+0, -2.14657E+1, -1.32635E+1, -1.20252E+1, -8.39589E+0,
                -5.806E+0, -1.14255E+1, 9.31738E-1, -5.21949E+0, -1.35669E+0, 2.6095E+0,
                -7.53639E+0, 9.28461E+0, -1.30485E+0, 4.80869E+0, 3.68795E+0, 4.64188E+0,
                4.53606E+0, 4.77124E+0, 5.13151E+0, 4.41101E+0, 4.87183E+0, 3.92511E+0,
                2.74209E+0, 7.61182E-1, 8.44631E-1, 0E+0
            }
        };

        // Precomputed values of s versus 1/ln(n) (here s's are the critical values of
        // the Jarque-Bera test and n's are the sample sizes) for each tabulated p-value
        double[][] crit_values =
        {
            new double[]
            {
                1.38155E+1, 1.65585E+1, 1.68158E+1, 1.71305E+1, 1.7519E+1, 1.80126E+1,
                1.86707E+1, 1.95539E+1, 2.1817E+1, 2.30771E+1, 2.48353E+1, 2.60239E+1,
                2.7514E+1, 2.84225E+1, 2.9429E+1, 3.06089E+1, 3.18884E+1, 3.23418E+1,
                3.28742E+1, 3.3261E+1, 3.35009E+1, 3.34825E+1, 3.32273E+1, 3.27514E+1,
                3.19343E+1, 3.06106E+1, 2.84885E+1, 2.50722E+1, 1.95425E+1, 1.09719E+1,
                1.8289E+0, 9.606E-1, 5.312E-1
            },
            new double[]
            {
                1.27921E+1, 1.48633E+1, 1.506E+1, 1.53029E+1, 1.56036E+1, 1.5983E+1,
                1.64879E+1, 1.71748E+1, 1.89068E+1, 1.98542E+1, 2.11758E+1, 2.20764E+1,
                2.31914E+1, 2.38582E+1, 2.46043E+1, 2.54748E+1, 2.6399E+1, 2.67278E+1,
                2.70975E+1, 2.73738E+1, 2.75458E+1, 2.74765E+1, 2.72686E+1, 2.69025E+1,
                2.62695E+1, 2.52501E+1, 2.36332E+1, 2.10001E+1, 1.67207E+1, 9.843E+0,
                1.8053E+0, 9.59E-1, 5.312E-1
            },
            new double[]
            {
                1.17687E+1, 1.32657E+1, 1.34102E+1, 1.35883E+1, 1.38149E+1, 1.4099E+1,
                1.44745E+1, 1.49865E+1, 1.62666E+1, 1.69669E+1, 1.79384E+1, 1.85877E+1,
                1.93922E+1, 1.98658E+1, 2.03979E+1, 2.101E+1, 2.16407E+1, 2.1877E+1,
                2.21135E+1, 2.22848E+1, 2.2379E+1, 2.22863E+1, 2.2103E+1, 2.18016E+1,
                2.13105E+1, 2.05242E+1, 1.92818E+1, 1.72981E+1, 1.40454E+1, 8.6751E+0,
                1.7727E+0, 9.564E-1, 5.312E-1
            },
            new double[]
            {
                1.07454E+1, 1.17666E+1, 1.18682E+1, 1.19883E+1, 1.21501E+1, 1.23493E+1,
                1.26143E+1, 1.29765E+1, 1.38903E+1, 1.43843E+1, 1.50633E+1, 1.55123E+1,
                1.60635E+1, 1.63846E+1, 1.67425E+1, 1.7146E+1, 1.7551E+1, 1.76977E+1,
                1.78305E+1, 1.79209E+1, 1.79431E+1, 1.78172E+1, 1.76594E+1, 1.74145E+1,
                1.70176E+1, 1.64088E+1, 1.54635E+1, 1.39762E+1, 1.15527E+1, 7.4815E+0,
                1.7276E+0, 9.52E-1, 5.312E-1
            },
            new double[]
            {
                9.722E+0, 1.03636E+1, 1.04296E+1, 1.05059E+1, 1.06072E+1, 1.07362E+1,
                1.09072E+1, 1.11441E+1, 1.17469E+1, 1.20743E+1, 1.25206E+1, 1.28124E+1,
                1.31643E+1, 1.33664E+1, 1.35878E+1, 1.38271E+1, 1.40491E+1, 1.41283E+1,
                1.41911E+1, 1.42092E+1, 1.41788E+1, 1.40345E+1, 1.38848E+1, 1.36749E+1,
                1.33572E+1, 1.28805E+1, 1.21658E+1, 1.10602E+1, 9.2814E+0, 6.2927E+0,
                1.6661E+0, 9.448E-1, 5.312E-1
            },
            new double[]
            {
                9.2103E+0, 9.6981E+0, 9.7478E+0, 9.8072E+0, 9.8811E+0, 9.9803E+0,
                1.01106E+1, 1.02914E+1, 1.07604E+1, 1.10157E+1, 1.13653E+1, 1.15923E+1,
                1.18602E+1, 1.20139E+1, 1.21804E+1, 1.23565E+1, 1.25067E+1, 1.25542E+1,
                1.25841E+1, 1.25801E+1, 1.25255E+1, 1.23739E+1, 1.22302E+1, 1.20355E+1,
                1.17488E+1, 1.13263E+1, 1.07058E+1, 9.7531E+0, 8.2365E+0, 5.7077E+0,
                1.6275E+0, 9.396E-1, 5.312E-1
            },
            new double[]
            {
                8.6987E+0, 9.0549E+0, 9.0911E+0, 9.1333E+0, 9.1869E+0, 9.2587E+0,
                9.3521E+0, 9.4852E+0, 9.8318E+0, 1.00226E+1, 1.02839E+1, 1.04542E+1,
                1.06494E+1, 1.07589E+1, 1.08779E+1, 1.09976E+1, 1.10882E+1, 1.11112E+1,
                1.11155E+1, 1.10884E+1, 1.10182E+1, 1.08586E+1, 1.07201E+1, 1.05399E+1,
                1.02818E+1, 9.9063E+0, 9.3658E+0, 8.5489E+0, 7.2613E+0, 5.135E+0,
                1.5828E+0, 9.329E-1, 5.311E-1
            },
            new double[]
            {
                7.6753E+0, 7.8296E+0, 7.8446E+0, 7.8611E+0, 7.8828E+0, 7.9127E+0,
                7.9501E+0, 8.0027E+0, 8.1454E+0, 8.2266E+0, 8.3384E+0, 8.4114E+0,
                8.4904E+0, 8.5307E+0, 8.5719E+0, 8.602E+0, 8.6004E+0, 8.585E+0, 8.552E+0,
                8.4921E+0, 8.3968E+0, 8.234E+0, 8.1098E+0, 7.952E+0, 7.7395E+0, 7.4472E+0,
                7.0389E+0, 6.4401E+0, 5.5238E+0, 4.0456E+0, 1.4723E+0, 9.133E-1, 5.31E-1
            },
            new double[]
            {
                7.3778E+0, 7.4872E+0, 7.4979E+0, 7.5089E+0, 7.5239E+0, 7.5441E+0,
                7.5691E+0, 7.604E+0, 7.6984E+0, 7.7526E+0, 7.8276E+0, 7.8751E+0,
                7.9259E+0, 7.9496E+0, 7.9718E+0, 7.9816E+0, 7.9589E+0, 7.9361E+0,
                7.8938E+0, 7.8286E+0, 7.7287E+0, 7.5661E+0, 7.4469E+0, 7.2949E+0,
                7.0942E+0, 6.8222E+0, 6.4463E+0, 5.8998E+0, 5.0729E+0, 3.7474E+0,
                1.4343E+0, 9.056E-1, 5.309E-1
            },
            new double[]
            {
                6.6519E+0, 6.6763E+0, 6.6777E+0, 6.6789E+0, 6.68E+0, 6.6832E+0, 6.6847E+0,
                6.6858E+0, 6.6882E+0, 6.6877E+0, 6.6842E+0, 6.6803E+0, 6.6688E+0,
                6.6571E+0, 6.6387E+0, 6.6071E+0, 6.5429E+0, 6.5028E+0, 6.4462E+0,
                6.3696E+0, 6.262E+0, 6.1045E+0, 5.9945E+0, 5.8598E+0, 5.6856E+0,
                5.4578E+0, 5.1501E+0, 4.7176E+0, 4.0773E+0, 3.0691E+0, 1.3294E+0,
                8.817E-1, 5.305E-1
            },
            new double[]
            {
                5.9915E+0, 5.9635E+0, 5.96E+0, 5.9546E+0, 5.9482E+0, 5.9408E+0, 5.9282E+0,
                5.9096E+0, 5.8581E+0, 5.8248E+0, 5.7728E+0, 5.7343E+0, 5.6783E+0,
                5.6408E+0, 5.5919E+0, 5.5277E+0, 5.4314E+0, 5.3796E+0, 5.3134E+0,
                5.2305E+0, 5.1203E+0, 4.9697E+0, 4.8689E+0, 4.7481E+0, 4.5973E+0,
                4.4039E+0, 4.1494E+0, 3.8011E+0, 3.2985E+0, 2.5239E+0, 1.2185E+0,
                8.519E-1, 5.297E-1
            },
            new double[]
            {
                5.6286E+0, 5.5806E+0, 5.5755E+0, 5.5677E+0, 5.5587E+0, 5.5471E+0,
                5.5296E+0, 5.5043E+0, 5.4336E+0, 5.3889E+0, 5.3194E+0, 5.2681E+0,
                5.1957E+0, 5.1474E+0, 5.0863E+0, 5.0071E+0, 4.8957E+0, 4.8378E+0,
                4.7662E+0, 4.6803E+0, 4.5688E+0, 4.4214E+0, 4.3258E+0, 4.2126E+0,
                4.0734E+0, 3.8968E+0, 3.6684E+0, 3.3596E+0, 2.9215E+0, 2.2555E+0,
                1.1516E+0, 8.316E-1, 5.29E-1
            },
            new double[]
            {
                4.6052E+0, 4.5289E+0, 4.5214E+0, 4.5105E+0, 4.4979E+0, 4.4814E+0,
                4.458E+0, 4.4246E+0, 4.332E+0, 4.2748E+0, 4.1873E+0, 4.1224E+0,
                4.0327E+0, 3.9732E+0, 3.8987E+0, 3.8025E+0, 3.673E+0, 3.6071E+0,
                3.5292E+0, 3.4374E+0, 3.3246E+0, 3.1834E+0, 3.0973E+0, 2.9987E+0,
                2.8827E+0, 2.7431E+0, 2.5707E+0, 2.3505E+0, 2.0533E+0, 1.6231E+0,
                9.442E-1, 7.553E-1, 5.251E-1
            },
            new double[]
            {
                3.7942E+0, 3.7173E+0, 3.7101E+0, 3.6997E+0, 3.6876E+0, 3.6718E+0, 3.65E+0,
                3.618E+0, 3.5318E+0, 3.479E+0, 3.3988E+0, 3.3402E+0, 3.2584E+0, 3.205E+0,
                3.1384E+0, 3.0523E+0, 2.9372E+0, 2.8788E+0, 2.8104E+0, 2.7298E+0,
                2.6316E+0, 2.5097E+0, 2.4361E+0, 2.3524E+0, 2.2547E+0, 2.139E+0,
                1.9986E+0, 1.8239E+0, 1.5965E+0, 1.2821E+0, 7.945E-1, 6.721E-1, 5.176E-1
            },
            new double[]
            {
                3.2189E+0, 3.1499E+0, 3.1433E+0, 3.1345E+0, 3.1237E+0, 3.1099E+0,
                3.0907E+0, 3.0631E+0, 2.9886E+0, 2.9434E+0, 2.8749E+0, 2.825E+0,
                2.7559E+0, 2.7106E+0, 2.6543E+0, 2.5819E+0, 2.4851E+0, 2.4361E+0,
                2.3789E+0, 2.3115E+0, 2.2297E+0, 2.1283E+0, 2.0674E+0, 1.998E+0,
                1.9172E+0, 1.8216E+0, 1.7063E+0, 1.5631E+0, 1.3779E+0, 1.1235E+0,
                7.302E-1, 6.303E-1, 5.074E-1
            },
            new double[]
            {
                2.7726E+0, 2.7129E+0, 2.7072E+0, 2.6996E+0, 2.6904E+0, 2.6787E+0,
                2.6624E+0, 2.6388E+0, 2.576E+0, 2.5382E+0, 2.4808E+0, 2.4388E+0,
                2.3812E+0, 2.3436E+0, 2.2964E+0, 2.2363E+0, 2.1557E+0, 2.1151E+0,
                2.0674E+0, 2.0114E+0, 1.9434E+0, 1.8592E+0, 1.8085E+0, 1.7508E+0,
                1.6835E+0, 1.604E+0, 1.5079E+0, 1.3885E+0, 1.2336E+0, 1.0198E+0, 6.878E-1,
                5.947E-1, 4.946E-1
            },
            new double[]
            {
                1.3863E+0, 1.3619E+0, 1.3595E+0, 1.3564E+0, 1.353E+0, 1.3484E+0,
                1.3421E+0, 1.3334E+0, 1.3097E+0, 1.2956E+0, 1.2744E+0, 1.2591E+0,
                1.2382E+0, 1.2243E+0, 1.2072E+0, 1.1852E+0, 1.1557E+0, 1.1408E+0,
                1.1231E+0, 1.1023E+0, 1.0768E+0, 1.0449E+0, 1.0256E+0, 1.0033E+0,
                9.771E-1, 9.457E-1, 9.071E-1, 8.577E-1, 7.916E-1, 6.951E-1, 5.285E-1,
                4.739E-1, 4.063E-1
            }
        };
        try
        {
            if (_n < 5) return SqlDouble.Null;
            double invlogn = 1.0 / Math.Log(_n);

            // An array of the Jarque-Bera test critical values at 1/ln(_n) for the tabulated p-values
            double[] criticalvalues = new double[pvals.Length];
            for (int i = 0; i < pvals.Length; i++)
            {
                CubicSpline spline = new CubicSpline(inv_log_n, crit_values[i], second_derivatives[i]);
                criticalvalues[i] = spline[invlogn];
            }
            Array.Reverse(criticalvalues);
            if (JBSt <= criticalvalues[0]) return new SqlDouble(pvals[0]);
            else if (JBSt >= criticalvalues[pvals.Length - 1]) return new SqlDouble(pvals[pvals.Length - 1]);
            else
            {
                // The p-value is approximated using the array of the Jarque-Bera test critical values
                // for the actual Jarque-Bera statistics of the sample.
                CubicSpline spline = new CubicSpline(criticalvalues, pvals);
                return new SqlDouble(spline[JBSt]);
            }
        }
        catch
        {
            return SqlDouble.Null;
        }
    }

    public void Init()
    {
        _n = 0;
        _s1 = 0.0;
        _s2 = 0.0;
        _s3 = 0.0;
        _s4 = 0.0;
    }

    public void Accumulate(SqlDouble val)
    {
        if (!val.IsNull)
        {
            ++_n;
            _s1 += (double)val;
            double prod = (double)val * (double)val;
            _s2 += prod;
            prod *= (double)val;
            _s3 += prod;
            prod *= (double)val;
            _s4 += prod;
        }
    }

    public void Merge(JBTest grp)
    {
        _n += grp._n;
        _s1 += grp._s1;
        _s2 += grp._s2;
        _s3 += grp._s3;
        _s4 += grp._s4;
    }

    public SqlDouble Terminate()
    {

        if (_n >= 5)
        {
            try
            {
                double m1 = _s1 / _n;
                double m1sqr = m1 * m1;
                double s2n = _s2 / _n;
                double s3n = _s3 / _n;
                double variance = s2n - m1sqr;
                double stddev = Math.Sqrt(variance);
                if (stddev != 0.0)
                {
                    double m3 = s3n - m1 * (3.0 * s2n - 2.0 * m1sqr);
                    double m4 = _s4 / _n - 4.0 * m1 * s3n + 3.0 * m1sqr * (2.0 * s2n - m1sqr);

                    double skewness = m3 / (variance * stddev);
                    double kurtosis = m4 / (variance * variance) - 3.0;
                    double jbstat = _n * (skewness * skewness +
                        kurtosis * kurtosis / 4.0) / 6.0;
                    return CalculatePValue(jbstat);
                }
            }
            catch (Exception)
            {
            }
        }

        return SqlDouble.Null;
    }

}

